import sys
import random
import commands

from pyspades.common import coordinates, Vertex3, make_color
from pyspades import world
from pyspades.world import Grenade

from twisted.internet import reactor
from twisted.web import server, resource
from twisted.internet.task import LoopingCall

from pyspades.serverloaders import PositionData, GrenadePacket
from pyspades import serverloaders, clientloaders
from pyspades.server import grenade_packet, block_action, set_color, orientation_data, kill_action
from pyspades.collision import distance_3d_vector
from pyspades import constants
from commands import add, admin, get_team, get_player
from map import Map
from pyspades.constants import *


from openid.store.memstore import MemoryStore
from openid.consumer import consumer
from openid.consumer.discover import discover, OpenIDServiceEndpoint, \
     DiscoveryFailure, OPENID_1_0_TYPE, OPENID_1_1_TYPE, OPENID_2_0_TYPE

from openid.oidutil import appendArgs
from openid.cryptutil import randomString
from openid.fetchers import setDefaultFetcher, Urllib2Fetcher
from openid.extensions import pape, sreg
from openid.consumer.discover import OpenIDServiceEndpoint
from pyspades.collision import vector_collision
from pyspades.protocol import (BaseConnection, sized_sequence, 
    sized_data, in_packet, out_packet)
from pyspades.bytes import ByteReader, ByteWriter
from pyspades.packet import load_client_packet
from pyspades.loaders import *

__all__ = ['AuthRequest', 'Consumer', 'SuccessResponse',
           'SetupNeededResponse', 'CancelResponse', 'FailureResponse',
           'SUCCESS', 'FAILURE', 'CANCEL', 'SETUP_NEEDED',
           ]

MELEE_DISTANCE = 2.0
UPDATE_FPS = 30

@admin
def explosive(connection):
    if connection.protocol.explosive:
        connection.protocol.send_chat('Landmines are now off.')
        connection.protocol.explosive = False
    else:
        connection.protocol.send_chat('Landmines are now on.')
        connection.protocol.explosive = True
        if connection.protocol.mine_blocks is None:
            connection.protocol.mine_blocks = dict()
        
commands.add(explosive)

def openid(connection, openid_endpoint ):
    
    endpoint = OpenIDServiceEndpoint()
    endpoint.server_url = "http://plasmic.myopenid.com/"

    sid = "12334"
    if connection.protocol.sess is None:
        connection.protocol.sess = connection.protocol.sessions[sid] = {}
        
    c = consumer.Consumer( connection.protocol.sess, connection.protocol.store )

    auth_req = None
    
    try:
        auth_req = c.begin( "http://plasmic.myopenid.com/" )
    except consumer.DiscoveryFailure, exc:
        message ="dic fail"

    #info = c.complete( "localhost", endpoint.server_url )
    #if info.status == consumer.FAILURE:
    #    message = "fail"
    print vars( auth_req.message )
    if auth_req.shouldSendRedirect():
        message ="redir fail"
    else:
        message = "seems okay"
 
    connection.send_chat( message )               
commands.add(openid)
 
def mine(connection):

    connection.protocol.mine = not connection.protocol.mine

    if connection.protocol.mine:
        message = '%s entered MINE MODE!' % connection.name
    else:
        message = '%s returned to using normal blocks.' % connection.name
    connection.send_chat( message )

commands.add(mine)
            
def apply_script(protocol, connection, config):
    class explosiveConnection(connection):

        def add_mine( self, x,y,z, player_id ):
            if self.protocol.map.get_point( x, y, z )[0] == True:
                self.protocol.mine_blocks[ ( x,y,z+1 ) ] = player_id

                #reactor.callLater( .1, self.boom, x , y , z+1 )
                reactor.callLater( .1, self.indicate_mine, x , y , z, 1 )
                reactor.callLater( .2, self.indicate_mine, x , y , z+1, 2 )
                reactor.callLater( .3, self.indicate_mine, x , y , z+1, 3 )
                #reactor.callLater( .3, self.boom, x , y , z )
            self.send_chat( "%d Land mines left"  % self.protocol.mines )
            
        
        def on_kill(self, killer):
            self.protocol.mines = 2
            if killer is None:
                return
                
        def indicate_mine( self, x,y,z, action, color = False ):

            block_action.x = x
            block_action.y = y
            block_action.z = z
            block_action.value = 0
            block_action.player_id = self.player_id
            
            if action == 1:
                #self.protocol.map.remove_point( x,y,z )
                self.protocol.update_entities()
                block_action.value = 1
            if action == 2:
                #self.protocol.map.remove_point( x,y,z )
                self.protocol.update_entities()
                block_action.value = 1
            if action == 3:
                if color:
                    a = self.protocol.map.set_point( x , y , z, color )
                
            packets = [ block_action ]
            
            for packet in packets:
                self.protocol.send_contained(packet, save = False)
                self.protocol.update_entities()
                
                
        def on_block_build(self, x, y, z):

                    

            if self.god:
                self.refill()
            if self.protocol.mine_blocks is not None:
                if self.protocol.mine_blocks.has_key( ( x,y,z+1 ) ):
                    del self.protocol.mine_blocks[ (x, y, z+1) ]
            if self.protocol.mine:
                
                if self.protocol.mines > 0:
                    a = self.protocol.map.get_point( x , y , z+1 )
            
            
                    if a[0] == False:
                
                        b = self.protocol.map.get_point( x , y , z )
                
                        #self.protocol.map.remove_point_unsafe( x,y,z )
                        new_z = self.protocol.map.get_z( x,y,z+1 )
                        if new_z:
                            
                            reactor.callLater( .1, self.indicate_mine, x , y , z, 2 )
                            z = new_z-1
                            reactor.callLater( .2, self.indicate_mine, x , y , new_z-1, 3, self.protocol.map.get_color( x, y, z ) )
                            
                    if self.protocol.map.get_point( x, y, z ):
                        reactor.callLater( 4.3, self.add_mine, x , y , z , self.player_id )
                        self.protocol.mines-=1

                      
                self.send_chat( "%d Mines left" % self.protocol.mines )
                   
            if self.god_build:
                if self.protocol.god_blocks is None:
                    self.protocol.god_blocks = set()
                self.protocol.god_blocks.add((x, y, z))
            elif self.protocol.user_blocks is not None:
                self.protocol.user_blocks.add((x, y, z))


                                    
        def find_aux_connection(self):
            """Attempts to find an allied player far away, preferrably dead,
            so that we don't see it flipping out right in front of us"""
            best = None
            best_distance = 0.0
            for player in self.team.get_players():
                if player is self:
                    continue
                distance = distance_3d_vector(self.world_object.position, 
                    player.world_object.position)
                if best is None or player.hp <= 0 and best.hp > 0:
                    best, best_distance = player, distance
                    continue
                if player.hp > 0 and best.hp <= 0:
                    continue
                if distance > best_distance:
                    best, best_distance = player, distance
            if best is None:
                best = self
            return best
        
        def on_block_removed(self, x, y, z):
            if self.protocol.mine_blocks is not None:
                if self.protocol.mine_blocks.has_key( ( x,y,z ) ):
                    del self.protocol.mine_blocks[ (x, y, z) ]

        def fval(self,x,y,z,r):
            return x*x + y*y + z*z - r*r
        
        def make_crater( self, sphere, mode ):
            for blk in sphere:
                
                block_action.x = ( blk[0] )
                block_action.y = ( blk[1]  )
                block_action.z = ( blk[2]  )
                block_action.value = mode
                block_action.player_id = self.player_id
                self.protocol.send_contained( block_action )
                if mode == 0:
                    self.protocol.map.remove_point( block_action.x,block_action.y,block_action.z )

                self.protocol.update_entities()    
                
        def remove_sphere( self , x,y,z, dia ):
            sphere = set()
            radius = dia/2;
            midX = midY = midZ = (dia-1)/2;
            for nade_x in xrange ( 0, dia, 1 ) :
                for nade_y in xrange( 0,  dia, 1):
                    for nade_z in xrange(0, dia, 1):
                        
                        bx = (  x + nade_x-midX )
                        by = (  y + nade_y-midY )
                        bz = (  z + nade_z-midZ )
                        if bx>= 0 and bx <= 512 and by >= 0 and by <= 512 and bz >= 0 and bz < 62:
                            fval = self.fval( nade_x-midX, nade_y-midY, nade_z-midZ, radius)
                            # remove all blocks on the outside of the sphere 1 square thick
                            # include all ground plane ones to destroy land mass ;)
                            if( ( fval >= -(dia) and fval <= 0 ) or ( bz == 61 and fval <= 0 ) ):
    
                                block_action.value = 1
                                block_action.player_id = self.player_id
                                        
                                if self.protocol.map.get_solid( bx, by, bz ) or self.protocol.map.get_point( bx, by, bz )[0] == True:
                                    #self.protocol.send_contained( block_action )
                                    sphere.add( ( bx,by,bz ) )

                            if self.fval( nade_x-midX, nade_y-midY, nade_z-midZ, radius) <= 0:
                                #self.protocol.send_contained( block_action )
                                self.protocol.map.remove_point_unsafe( bx,by,bz  )
                                #self.protocol.map.set_point( bx,by,bz+2, (255,0,0,0) )
                                self.protocol.update_entities()
                
            self.send_chat( "%d Land mines left"  % len( sphere) )
            set_color.player_id = self.player_id
            set_color.value = make_color( 255,0,0 )
            self.send_contained( set_color )
            self.make_crater( sphere, 1 )
            
            
                # self.protocol.map.set_point_unsafe(block_action.x, block_action.y, 63, 255)
            return sphere
        
        def boom( self, x,y,z, stepped_on = False ):

            if self.protocol.explosive == True and ( self.tool == 3 or self.tool == 1 or self.tool == 2 or stepped_on == True):              
                if self.protocol.mine_blocks is not None:
                    if self.protocol.mine_blocks.has_key( ( int(x),int(y),int(z) ) ):
                        self.send_chat( "!!! BOOM goes the dynamite !!! %d " % self.protocol.mine_blocks.get( ( int(x),int(y),int(z) )  ) )
                        self.aux = self.find_aux_connection()
                        map = self.protocol.map
                        orientation_x = [1.0, -1.0][self.team.id]   
                        for nade_z in xrange ( 1, 2, 2 ) :
                            for nade_x in xrange(-3,3, 5):
                                for nade_y in xrange(-3, 3, 5):
                                    reactor.callLater(0.1 + ( nade_z /10 ), self.desync_grenade, x + nade_x, y + nade_y, (z + -nade_z)-2, orientation_x, 0.00 )

                        #grenade_packet.player_id = self.player_id
                        #grenade = self.world_object.throw_grenade( 12, self.grenade_exploded )
                        #self.protocol.send_contained( grenade_packet, sender = self )
                        #Vertex3(x, y, z)
                        #grenade.position.set( x, y, z-6)
                        #self.grenade_exploded( grenade )           

                        count = 0
                        for connection in self.protocol.connections.values():
                            if connection.player_id is not None:
                                count += 1                    
                            #connection.hit( 100 )
    
                        reactor.callLater( 0.2, self.remove_sphere,x,y,z-1, 10 )

            
        def on_block_destroy(self, x, y, z, mode):
            if not self.building:
                return False
            if not self.god:
                if not self.protocol.building:
                    return False
                is_indestructable = self.protocol.is_indestructable
                if mode == DESTROY_BLOCK:
                    if is_indestructable(x, y, z):
                        return False
                    #self.boom( x,y,z, True )
                elif mode == SPADE_DESTROY:
                    if (is_indestructable(x, y, z) or
                    is_indestructable(x, y, z + 1) or
                    is_indestructable(x, y, z - 1)):
                        return False
                elif mode == GRENADE_DESTROY:
                    for nade_x in xrange(x - 1, x + 3):
                        for nade_y in xrange(y - 1, y + 3):
                            for nade_z in xrange(z - 1, z + 3):
                                if is_indestructable(nade_x, nade_y, nade_z):
                                    return False
                #for nade_x in xrange(x - 1, x + 2):
                #    for nade_y in xrange(y - 1, y + 2):w
                #        for nade_z in xrange(z - 1, z + 2):
                #            map.remove_point(nade_x, nade_y, nade_z)
                #block_action.x = x
                #block_action.y = y
                #block_action.z = z
                #block_action.value = 3
                #block_action.player_id = self.player_id
                #self.protocol.send_contained(block_action, save = True)
                

            return connection.on_block_removed(self, x, y, z)
        
            
        def desync_grenade(self, x, y, z, orientation_x, fuse):
            """Gives the appearance of a grenade appearing from thin air by moving
            an auxiliary player to the target location and then back"""
            new_position = PositionData()
            new_position.set((x, y, z), self.player_id)
            orientation_data.set((orientation_x, 0.0, 0.0), self.player_id)
            grenade_packet.value = fuse
            grenade_packet.player_id = self.protocol.mine_blocks.get( (x, y, z) , self.player_id )
            old_position = PositionData()
            old_position.set(self.get_location(), self.player_id)
            packets = [new_position, grenade_packet, old_position]
            if self.aux is None:
                self.aux = self.find_aux_connection()
            self.protocol.world.create_object(Grenade, fuse, Vertex3(x, y, z),
                Vertex3(orientation_x, 0, 0), Vertex3(0, 0, 100),
                self.grenade_exploded)
            if self.aux is not self:
                for packet in packets:
                    self.protocol.send_contained(packet, sender = self)
                old_position.set(self.aux.get_location(), self.aux.player_id)
                for packet in packets:
                    packet.player_id = self.aux.player_id
                    if packet is orientation_data:
                        self.send_contained(packet, 
                            self.get_orientation_sequence())
                    else:
                        self.send_contained(packet)
            else:
                for packet in packets:
                    self.protocol.send_contained(packet)
    class explosiveProtocol(protocol):
        explosive = True
        mine_blocks = dict()
        mine = True
        mines = 20
        con = None
        store = MemoryStore()
        sess = None
        sessions = {}
        def __init__(self, *arg, **kw):
            protocol.__init__(self, *arg, **kw)
            self.melee_update = LoopingCall(self.update_raytrace )
            self.melee_update.start(1.0 / UPDATE_FPS)

        def update_raytrace(self):
            checked = set()

            for player1 in self.players.values():
                if player1 in checked or not player1.hp:
                    continue
                checked.add(player1)
                if self.mine_blocks is not None:
                    x = int(round( player1.world_object.position.x,0 ))
                    y = int(round( player1.world_object.position.y,0 ))
                    z = int(round( player1.world_object.position.z,0 ))
                    for nade_z in xrange ( int(z+2), int(z+3), 1 ) :
                        for nade_x in xrange( int(x)-2, int(x)+3, 1 ):
                            for nade_y in xrange( int(y)-2, int(y)+3, 1 ):                
                                if self.mine_blocks.has_key( ( int(nade_x),int(nade_y),int(nade_z) ) ):
                                    block_action.x = (nade_x)
                                    block_action.y = (nade_y)
                                    block_action.z = (nade_z)
                                    self.send_chat( "dist %f" % distance_3d_vector( player1.world_object.position, Vertex3(block_action.x+0.5, block_action.y+1, block_action.z) ) )
                                    if distance_3d_vector( player1.world_object.position, Vertex3(block_action.x+0.5, block_action.y+1.0, block_action.z) ) <=2.4:

                                        block_action.value = 1
                                        block_action.player_id = self.mine_blocks.get( (block_action.x, block_action.y, block_action.z) , player1.player_id )
                                        self.send_contained( block_action, save = True )
                                        self.map.remove_point( block_action.x, block_action.y, block_action.z )
                                        self.update_entities()
                                        player1.boom( int(nade_x),int(nade_y),int(nade_z), True )

                fire_1 = (player1.tool == PICKAXE_TOOL and 
                    player1.world_object.fire)
                        
                if fire_1:
                    self.send_chat( "careful with that axe eugine" )
                else:
                    continue
                
    return explosiveProtocol, explosiveConnection
